package com.uuah.server;

import java.util.Collection;

import javax.resource.spi.work.WorkListener;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.uuah.api.config.IThreadingProfile;
import com.uuah.api.lifecycle.IDisposable;
import com.uuah.api.lifecycle.IStartable;
import com.uuah.api.lifecycle.IStoppable;
import com.uuah.api.lifecycle.Initialisable;
import com.uuah.api.work.IWorkManager;
import com.uuah.config.i18n.CoreMessages;
import com.uuah.exception.AbstractUuahException;
import com.uuah.exception.UuahRuntimeException;
import com.uuah.exception.lifecycle.InitialisationException;
import com.uuah.queue.IQueueManager;
import com.uuah.server.api.IUuahContext;
import com.uuah.server.api.config.IUuahConfiguration;
import com.uuah.server.api.config.registry.IRegistry;
import com.uuah.server.api.config.registry.IRegistryBroker;
import com.uuah.server.api.config.registry.IUuahRegistry;
import com.uuah.server.api.context.notification.linstener.IServerNotificationListener;
import com.uuah.server.api.lifecycle.ILifecycleManager;
import com.uuah.server.config.Constants;
import com.uuah.server.config.i18n.ServerMessages;
import com.uuah.server.config.registry.RegistryBroker;
import com.uuah.server.config.registry.UuahRegistry;
import com.uuah.server.context.notification.ServerNotification;
import com.uuah.server.context.notification.ServerNotificationManager;
import com.uuah.server.context.notification.UuahContextNotification;
import com.uuah.server.exception.context.notification.NotificationException;
import com.uuah.server.exception.registry.RegistrationException;
import com.uuah.server.util.ServerShutdownSplashMsg;
import com.uuah.server.util.ServerStartupSplashMsg;
import com.uuah.server.util.SplashMsg;

public class UuahContext implements IUuahContext {
	private static transient Logger logger = LoggerFactory
			.getLogger(UuahContext.class);

	private IRegistryBroker registryBroker;

	private IUuahRegistry uuahRegistry;

	private IWorkManager workManager;

	private WorkListener workListener;

	protected ILifecycleManager lifecycleManager;

	protected ServerNotificationManager notificationManager;

	private IUuahConfiguration config;

	private long startDate;

	public UuahContext(IUuahConfiguration config, IWorkManager workManager,
			WorkListener workListener, ILifecycleManager lifecycleManager,
			ServerNotificationManager notificationManager) {
		this.config = config;
		this.workManager = workManager;
		this.workListener = workListener;
		this.lifecycleManager = lifecycleManager;
		this.notificationManager = notificationManager;
	}

	protected IRegistryBroker createRegistryBroker() {
		return new RegistryBroker();
	}

	protected IUuahRegistry createRegistry(IRegistry registry) {
		return new UuahRegistry(registry);
	}

	public void initialise() throws InitialisationException {
		lifecycleManager.checkPhase(Initialisable.PHASE_NAME);

		if (getNotificationManager() == null) {
			throw new UuahRuntimeException(CoreMessages
					.objectIsNull("notifactionManager"));
		}
		if (workManager == null) {
			throw new UuahRuntimeException(CoreMessages
					.objectIsNull("workManager"));
		}

		try {
			registryBroker = createRegistryBroker();
			uuahRegistry = createRegistry(registryBroker);

			registryBroker.initialise();

			workManager.start();
			getNotificationManager().start(workManager, workListener);

			fireNotification(new UuahContextNotification(this,
					UuahContextNotification.CONTEXT_INITIALISING));

			lifecycleManager.firePhase(this, Initialisable.PHASE_NAME);

			fireNotification(new UuahContextNotification(this,
					UuahContextNotification.CONTEXT_INITIALISED));
		} catch (Exception e) {
			throw new InitialisationException(e, this);
		}
	}

	public synchronized void start() throws AbstractUuahException {
		lifecycleManager.checkPhase(IStartable.PHASE_NAME);
		if (!isStarted()) {
			if (getQueueManager() == null) {
				throw new UuahRuntimeException(CoreMessages
						.objectIsNull("queueManager"));
			}

			startDate = System.currentTimeMillis();

			fireNotification(new UuahContextNotification(this,
					UuahContextNotification.CONTEXT_STARTING));

			lifecycleManager.firePhase(this, IStartable.PHASE_NAME);

			fireNotification(new UuahContextNotification(this,
					UuahContextNotification.CONTEXT_STARTED));

			if (logger.isInfoEnabled()) {
				SplashMsg splashScreen = SplashMsg
						.getInstance(ServerStartupSplashMsg.class);
				splashScreen.setHeader(this);
				splashScreen.setFooter(this);
				logger.warn(splashScreen.toString());
			}
		}
	}

	public synchronized void stop() throws AbstractUuahException {
		lifecycleManager.checkPhase(IStoppable.PHASE_NAME);
		fireNotification(new UuahContextNotification(this,
				UuahContextNotification.CONTEXT_STOPPING));
		lifecycleManager.firePhase(this, IStoppable.PHASE_NAME);
		fireNotification(new UuahContextNotification(this,
				UuahContextNotification.CONTEXT_STOPPED));
	}

	public void dispose() {
		if (isDisposing()) {
			return;
		}

		ServerNotificationManager notificationManager = getNotificationManager();
		lifecycleManager.checkPhase(IDisposable.PHASE_NAME);
		fireNotification(new UuahContextNotification(this,
				UuahContextNotification.CONTEXT_DISPOSING));

		try {
			if (isStarted()) {
				stop();
			}
		} catch (AbstractUuahException e) {
			logger.error("Failed to stop manager: " + e.getMessage(), e);
		}

		try {
			lifecycleManager.firePhase(this, IDisposable.PHASE_NAME);
			registryBroker.dispose();
		} catch (Exception e) {
			logger.debug("Failed to cleanly dispose Uuah: " + e.getMessage(), e);
		}

		notificationManager.fireNotification(new UuahContextNotification(this,
				UuahContextNotification.CONTEXT_DISPOSED));

		notificationManager.dispose();
		workManager.dispose();

		if ((getStartDate() > 0) && logger.isInfoEnabled()) {
			SplashMsg splashScreen = SplashMsg
					.getInstance(ServerShutdownSplashMsg.class);
			splashScreen.setHeader(this);
			logger.info(splashScreen.toString());
		}

	}

	public boolean isInitialised() {
		return lifecycleManager.isPhaseComplete(Initialisable.PHASE_NAME);
	}

	public boolean isInitialising() {
		return IDisposable.PHASE_NAME.equals(lifecycleManager
				.getExecutingPhase());
	}

	protected boolean isStopped() {
		return lifecycleManager.isPhaseComplete(IStoppable.PHASE_NAME);
	}

	protected boolean isStopping() {
		return IStoppable.PHASE_NAME.equals(lifecycleManager
				.getExecutingPhase());
	}

	public boolean isStarted() {
		return lifecycleManager.isPhaseComplete(IStartable.PHASE_NAME);
	}

	protected boolean isStarting() {
		return IStartable.PHASE_NAME.equals(lifecycleManager
				.getExecutingPhase());
	}

	public boolean isDisposed() {
		return lifecycleManager.isPhaseComplete(IDisposable.PHASE_NAME);
	}

	public boolean isDisposing() {
		return IDisposable.PHASE_NAME.equals(lifecycleManager
				.getExecutingPhase());
	}

	public ILifecycleManager getLifecycleManager() {
		return lifecycleManager;
	}

	public void registerListener(IServerNotificationListener l)
			throws NotificationException {
		registerListener(l, null);
	}

	public void registerListener(IServerNotificationListener l,
			String resourceIdentifier) throws NotificationException {
		ServerNotificationManager notificationManager = getNotificationManager();
		if (notificationManager == null) {
			throw new UuahRuntimeException(ServerMessages
					.serverNotificationManagerNotEnabled());
		}
		notificationManager.addListenerSubscription(l, resourceIdentifier);
	}

	public void unregisterListener(IServerNotificationListener l) {
		ServerNotificationManager notificationManager = getNotificationManager();
		if (notificationManager != null) {
			notificationManager.removeListener(l);
		}
	}

	public void fireNotification(ServerNotification notification) {
		ServerNotificationManager notificationManager = getNotificationManager();
		if (notificationManager != null) {
			notificationManager.fireNotification(notification);
		} else if (logger.isDebugEnabled()) {
			logger
					.debug("UuahEvent Manager is not enabled, ignoring notification: "
							+ notification);
		}
	}

	public IWorkManager getWorkManager() {
		return workManager;
	}

	public WorkListener getWorkListener() {
		return workListener;
	}

	public IQueueManager getQueueManager() {
		IQueueManager queueManager = (IQueueManager) registryBroker
				.lookupObject(Constants.OBJECT_QUEUE_MANAGER);
		if (queueManager == null) {
			Collection temp = registryBroker.lookupObjects(IQueueManager.class);
			if (temp.size() > 0) {
				queueManager = ((IQueueManager) temp.iterator().next());
			}
		}
		return queueManager;
	}

	public void setQueueManager(IQueueManager queueManager)
			throws RegistrationException {
		checkLifecycleForPropertySet(Constants.OBJECT_QUEUE_MANAGER,
				Initialisable.PHASE_NAME);
		registryBroker.registerObject(Constants.OBJECT_QUEUE_MANAGER,
				queueManager);
	}

	public IUuahConfiguration getConfiguration() {
		return config;
	}

	public ServerNotificationManager getNotificationManager() {
		return notificationManager;
	}

	public void register() throws RegistrationException {
		throw new UnsupportedOperationException("register");
	}

	public void deregister() throws RegistrationException {
		throw new UnsupportedOperationException("deregister");
	}

	public String getRegistryId() {
		throw new UnsupportedOperationException("registryId");
	}

	protected void checkLifecycleForPropertySet(String propertyName,
			String phase) throws IllegalStateException {
		if (lifecycleManager.isPhaseComplete(phase)) {
			throw new IllegalStateException("Cannot set property: '"
					+ propertyName
					+ "' once the server has been gone through the " + phase
					+ " phase.");
		}
	}

	public IUuahRegistry getRegistry() {
		return uuahRegistry;
	}

	public IThreadingProfile getDefaultMessageDispatcherThreadingProfile() {
		return (IThreadingProfile) getRegistry().lookupObject(
				Constants.OBJECT_DEFAULT_MESSAGE_DISPATCHER_THREADING_PROFILE);
	}

	public IThreadingProfile getDefaultMessageReceiverThreadingProfile() {
		return (IThreadingProfile) getRegistry().lookupObject(
				Constants.OBJECT_DEFAULT_MESSAGE_RECEIVER_THREADING_PROFILE);
	}

	public IThreadingProfile getDefaultServiceThreadingProfile() {
		return (IThreadingProfile) getRegistry().lookupObject(
				Constants.OBJECT_DEFAULT_SERVICE_THREADING_PROFILE);
	}

	public IThreadingProfile getDefaultThreadingProfile() {
		return (IThreadingProfile) getRegistry().lookupObject(
				Constants.OBJECT_DEFAULT_THREADING_PROFILE);
	}

	public void addRegistry(long id, IRegistry registry) {
		registryBroker.addRegistry(id, registry);
	}

	public void removeRegistry(long id) {
		registryBroker.removeRegistry(id);
	}

	public long getStartDate() {
		return startDate;
	}
}
